package com.ctl;

import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.*;
import org.mybatis.generator.codegen.XmlConstants;
import org.mybatis.generator.codegen.mybatis3.ListUtilities;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.config.PropertyRegistry;
import java.util.*;


/**
 * <p>Title: run</p>
 * <p>Description: 自定义扩展mapper和xml插件</p>
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: https://gitee.com/ctllin</p>
 *
 * @author ctl
 * @version 1.0
 * @date 2022-04-01 10:09
 */
public class MyExtendPlugin extends PluginAdapter {

    public static final String METHOD_BATCH_INSERT = "batchInsertExtend";  // 方法名
    public static final String METHOD_BATCH_INSERT_SELECTIVE = "batchInsertSelectiveExtend";  // 方法名
    public static final String EXTEND = "Extend";
    private String targetXMLProject = null;
    private String targetJavaProject = null;
    private String targetXMLPackage = null;
    private String targetJavaPackage = null;


    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
        targetXMLProject = properties.getProperty("targetXMLProject");
        targetJavaProject = properties.getProperty("targetJavaProject");
        targetXMLPackage = properties.getProperty("targetXMLPackage");
        targetJavaPackage = properties.getProperty("targetJavaPackage");
    }

    @Override
    public List<GeneratedXmlFile> contextGenerateAdditionalXmlFiles(IntrospectedTable introspectedTable) {
        Document document = new Document(
                XmlConstants.MYBATIS3_MAPPER_PUBLIC_ID,
                XmlConstants.MYBATIS3_MAPPER_SYSTEM_ID);
        //com.ctl.mapper.TbTestMapper
        String mapperFullName = introspectedTable.getMyBatis3JavaMapperType();
        XmlElement root = new XmlElement("mapper"); //$NON-NLS-1$
        root.addAttribute(new Attribute("namespace", mapperFullName + EXTEND));
        document.setRootElement(root);

        sqlMapDocumentGenerated(document,introspectedTable);

        //home/gitee/mybatis-generator-core/src/main/resources/com/ctl/mapper/TbTestMapperExtend.xml
        GeneratedXmlFile gxf = new GeneratedXmlFile(document, properties
                .getProperty("fileName", introspectedTable.getMyBatis3XmlMapperFileName().split("\\.")[0] + EXTEND + ".xml"),
                targetXMLPackage, //$NON-NLS-1$
                targetXMLProject, //$NON-NLS-1$
                false, context.getXmlFormatter());

        List<GeneratedXmlFile> answer = new ArrayList<>(1);
        answer.add(gxf);
        return answer;
    }


    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        // 1. batchInsert
        XmlElement batchInsertEle = new XmlElement("insert");
        batchInsertEle.addAttribute(new Attribute("id", METHOD_BATCH_INSERT));
        // 参数类型
        batchInsertEle.addAttribute(new Attribute("parameterType", "map"));
        batchInsertEle.addElement(new TextElement("insert into " + introspectedTable.getFullyQualifiedTableNameAtRuntime()));
        List<IntrospectedColumn> allColumns = introspectedTable.getAllColumns();
        StringBuffer buffer = new StringBuffer();
        buffer.append("(");
        for (int i = 0; i < allColumns.size(); i++) {
            IntrospectedColumn introspectedColumn = allColumns.get(i);
            if (i != allColumns.size() - 1) {
                buffer.append(introspectedColumn.getActualColumnName()).append(",");
            } else {
                buffer.append(introspectedColumn.getActualColumnName());
            }
        }
        buffer.append(")");

        batchInsertEle.addElement(new TextElement(buffer.toString()));
        StringBuilder valuesClause = new StringBuilder();
        valuesClause.append("("); //$NON-NLS-1$
        List<IntrospectedColumn> columns = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns());
        for (int i = 0; i < columns.size(); i++) {
            IntrospectedColumn introspectedColumn = columns.get(i);
            valuesClause.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn,"item."));
            if (i + 1 < columns.size()) {
                valuesClause.append(", "); //$NON-NLS-1$
            }
        }

        valuesClause.append(')');
        // 添加foreach节点
        XmlElement foreachElement = new XmlElement("foreach");
        foreachElement.addAttribute(new Attribute("collection", "list"));
        foreachElement.addAttribute(new Attribute("item", "item"));
        foreachElement.addAttribute(new Attribute("separator", ","));
        foreachElement.addElement(new TextElement(valuesClause.toString()));
        // values 构建
        batchInsertEle.addElement(new TextElement("values"));
        batchInsertEle.addElement(foreachElement);
        document.getRootElement().addElement(batchInsertEle);

        // 2. batchInsertSelective
        XmlElement batchInsertSelectiveEle = new XmlElement("insert");
        batchInsertSelectiveEle.addAttribute(new Attribute("id", METHOD_BATCH_INSERT_SELECTIVE));
        // 参数类型
        batchInsertSelectiveEle.addAttribute(new Attribute("parameterType", "map"));
        batchInsertSelectiveEle.getElements().addAll(this.generateSelectiveEnhancedEles(introspectedTable));
        document.getRootElement().addElement(batchInsertSelectiveEle);
        return true;
    }

    /**
     * 生成insert selective 增强的插入语句
     *
     * @param introspectedTable
     * @return
     */
    private List<VisitableElement> generateSelectiveEnhancedEles(IntrospectedTable introspectedTable) {
        List<VisitableElement> eles = new ArrayList<>();
        eles.add(new TextElement("insert into " + introspectedTable.getFullyQualifiedTableNameAtRuntime() + " ("));
        XmlElement foreachInsertColumns = new XmlElement("foreach");
        foreachInsertColumns.addAttribute(new Attribute("collection", "selective"));
        foreachInsertColumns.addAttribute(new Attribute("item", "column"));
        foreachInsertColumns.addAttribute(new Attribute("separator", ","));
        foreachInsertColumns.addElement(new TextElement("${column.column}"));
        eles.add(foreachInsertColumns);
        eles.add(new TextElement(")"));
        // values
        eles.add(new TextElement("values"));
        // foreach values
        XmlElement foreachValues = new XmlElement("foreach");
        foreachValues.addAttribute(new Attribute("collection", "list"));
        foreachValues.addAttribute(new Attribute("item", "item"));
        foreachValues.addAttribute(new Attribute("separator", ","));
        foreachValues.addElement(new TextElement("("));
        // foreach 所有插入的列，比较是否存在
        XmlElement foreachInsertColumnsCheck = new XmlElement("foreach");
        foreachInsertColumnsCheck.addAttribute(new Attribute("collection", "selective"));
        foreachInsertColumnsCheck.addAttribute(new Attribute("item", "column"));
        foreachInsertColumnsCheck.addAttribute(new Attribute("separator", ","));

        // 所有表字段
        List<IntrospectedColumn> columns = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns());
        List<IntrospectedColumn> columns1 = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns());
        for (int i = 0; i < columns1.size(); i++) {
            IntrospectedColumn introspectedColumn = columns.get(i);
            XmlElement check = new XmlElement("if");
            check.addAttribute(new Attribute("test", "'" + introspectedColumn.getActualColumnName() + "'.toString() == column.column"));
            check.addElement(new TextElement(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn, "item.")));
            foreachInsertColumnsCheck.addElement(check);
        }
        foreachValues.addElement(foreachInsertColumnsCheck);
        foreachValues.addElement(new TextElement(")"));
        eles.add(foreachValues);
        return eles;
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        String superType = introspectedTable.getMyBatis3JavaMapperType();
        String extendMapperName = superType + "Extend";
        if (Objects.nonNull(targetJavaPackage) && Objects.nonNull(targetJavaProject)) {
            List<GeneratedJavaFile> files = new ArrayList<>();
            //Get entity class name
            String recordType = introspectedTable.getBaseRecordType();
            String[] entityPackage = recordType.split("\\.");
            String className = entityPackage[entityPackage.length - 1];
            //Create interface
//            String servicePackage = targetPackage + "." + className + "Service";
            FullyQualifiedJavaType service = new FullyQualifiedJavaType(extendMapperName);
            Interface interfaze = new Interface(service);
            interfaze.setVisibility(JavaVisibility.PUBLIC);


            if (Objects.nonNull(extendMapperName)) {
                //Import package
                interfaze.addImportedType(new FullyQualifiedJavaType(extendMapperName));
                //Interface name
                String[] basicServicePackage = extendMapperName.split("\\.");
                FullyQualifiedJavaType interfacePackage = new FullyQualifiedJavaType(superType);
                //接口添加泛型格式BaseService<实体,主键类型>
//                serviceInterface.addImportedType(new FullyQualifiedJavaType(recordType));
//                interfacePackage.addTypeArgument(new FullyQualifiedJavaType(recordType));
//                Optional<FullyQualifiedJavaType> optional = primaryKeyType(introspectedTable);
//                if (optional.isPresent()) {
//                    FullyQualifiedJavaType javaType = optional.get();
//                    if (javaType.isExplicitlyImported()) {
//                        serviceInterface.addImportedType(javaType);
//                    }
//                    interfacePackage.addTypeArgument(javaType);
//                }
                interfaze.addSuperInterface(interfacePackage);
                interfaze.addImportedType(new FullyQualifiedJavaType("java.util.List"));
                interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));
                interfaze.addImportedType(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));//po 例如 com.ctl.po.TbTest

                GeneratedJavaFile javaFile = new GeneratedJavaFile(interfaze, targetJavaProject,
                        context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                        context.getJavaFormatter());
                files.add(javaFile);

                // 1. batchInsert
                FullyQualifiedJavaType listType = FullyQualifiedJavaType.getNewListInstance();
                listType.addTypeArgument(introspectedTable.getRules().calculateAllFieldsClass());
                Method batchInsertSelectiveMethod = new Method(METHOD_BATCH_INSERT_SELECTIVE);
                batchInsertSelectiveMethod.setReturnType(FullyQualifiedJavaType.getIntInstance());
                batchInsertSelectiveMethod.setVisibility(JavaVisibility.DEFAULT);
                batchInsertSelectiveMethod.addParameter(new Parameter(listType, "list", "@Param(\"list\")"));
                FullyQualifiedJavaType selectiveType = new FullyQualifiedJavaType(introspectedTable.getRules().calculateAllFieldsClass().getShortName() + "." + MyColumnPlugin.ENUM_NAME);
                batchInsertSelectiveMethod.addParameter(new Parameter(selectiveType, "selective", "@Param(\"selective\")", true));
                batchInsertSelectiveMethod.setAbstract(true);

                Method batchInsertMethod = new Method(METHOD_BATCH_INSERT);
                batchInsertMethod.setReturnType(FullyQualifiedJavaType.getIntInstance());
                batchInsertMethod.setVisibility(JavaVisibility.DEFAULT);
                batchInsertMethod.addParameter(new Parameter(listType, "list", "@Param(\"list\")"));
                batchInsertMethod.setAbstract(true);

                interfaze.addMethod(batchInsertSelectiveMethod);
                interfaze.addMethod(batchInsertMethod);


            }

            return files;
        }
        return Collections.emptyList();
    }

}